1장. 결합과 시스템 설계

출처: 『소프트웨어 설계의 결합 균형』(블라드 코노노프 지음, 장연호 옮김, 제이펍 2026) | 공식: https://www.jpub.kr/ | 원서: Manning

"결합(coupling)"은 흔히 나쁜 설계의 동의어처럼 쓰인다. 하지만 결합이 0인 시스템은 아예 작동하지 않는다. 이 장은 결합이 무엇이고, 어떻게 시스템을 묶어주는 "접착제" 역할을 하는지, 두려워하기보다 설계 도구로 다루는 관점을 잡아준다.

학습 목표

이 장을 끝내면 다음을 할 수 있다.

  • 결합(coupling)을 어원·정의 차원에서 설명한다.
  • 결합의 두 동인(공유 수명주기·공유 지식)을 구분하고 각각이 변경 전파에 어떻게 작용하는지 분석한다.
  • 같은 기능을 공유 지식 양이 다른 세 가지 인터페이스로 재설계한다(MySQLRepository → IRepository(SQL) → IRepository(Save/Query)).
  • 시스템의 세 구성요소(구성요소·상호작용·목적)와 결합의 관계를 설명한다.
  • 코드베이스에서 "필수 결합"과 "우연 결합"의 차이를 판단한다.

전체 흐름도

[ 결합이란? — 라틴어 copulare = "함께 + 고정" = 연결 ]
        │
        ▼
[ 결합의 규모 = 얼마나 강하게 함께 변하는가 ]
        │
        ├─ 공유 수명주기  (같이 빌드·테스트·배포되는가)
        └─ 공유 지식      (서로에 대해 얼마나 아는가)
                │
                ▼
        [ 지식 흐름 ]
          상위(upstream)  ── 기능 제공
              ▲
              │  하위가 상위의 인터페이스를 "안다"
              │
          하위(downstream) ── 기능 소비

        ▼
[ 시스템 = 구성요소 + 상호작용 + 목적 ]
   결합 = 구성요소를 묶어 목적을 달성하게 하는 "접착제"

        ▼
[ 핵심 결론 ]
   결합 0 = 불가능 + 무용. 다만 결합도 *필수*인지 *우연*인지 구분해야 한다.
   "필수는 신중히, 우연은 제거" → 이후 장의 모든 설계 원리의 토대.

0. 사전 필수 용어

  • 결합 (Coupling) — 두 구성요소가 연결되어 있어 서로 영향을 주는 관계. "결합됨" = "연결됨"으로 바꿔 읽어도 무방.
  • 공유 수명주기 (Shared Lifecycle) — 두 구성요소가 함께 빌드·테스트·배포되는 정도. 한 모놀리스 안에 있으면 강함, 별 서비스로 떨어지면 약함.
  • 공유 지식 (Shared Knowledge) — 한쪽이 다른 쪽의 내부에 대해 알아야 하는 양. 인터페이스 서명·기능 요구·구현 세부·환경 가정 등 다양한 형태.
  • 상위 (Upstream) / 하위 (Downstream) — 기능을 제공하는 쪽이 상위, 소비하는 쪽이 하위. 종속성 화살표는 하위→상위, 지식 흐름은 그 반대(상위→하위).
  • 시스템 (System) — 메도즈의 정의: "무언가를 달성하는 방식으로 구성된 상호 연결된 요소들의 집합." 세 요소: 구성요소·상호작용·목적.
  • 필수 결합 vs 우연 결합 — 시스템의 목적상 반드시 필요한 결합과, 설계 부주의로 우연히 생긴 결합. 후자가 제거 대상.
  • 허용 오차 (Tolerance) — 기계공학 비유. 결합부의 허용 가능한 변화 한계. 너무 빡빡하면 깨지고, 너무 헐거우면 흔들린다.
  • 캡슐화 경계 (Encapsulation Boundary) — 구성요소가 외부에 드러내는 면과 숨기는 면의 구분선. 경계 바깥으로 새어나온 지식이 곧 우연 결합의 씨앗이 된다.

1. 결합이란? — 어원에서 본질로

결합은 영어 "couple"의 라틴어 어원 copulare에서 왔다. co(함께) + apere(고정) — "함께 고정한다"는 뜻이다. 즉 두 개체가 어떤 식으로든 연결되어 서로에게 영향을 줄 수 있는 모든 관계가 결합이다.

라틴어: co(함께) + apere(고정)
  └──▶ copulare / copula
         └──▶ 프랑스 고어: cople / copler
                └──▶ 영어: couple → coupling

이 정의는 의외로 광범위하다.

  • 시계 — 기어·스프링이 결합돼 시간을 측정한다.
  • 자동차 — 엔진·차축·바퀴·브레이크가 결합돼 굴러간다.
  • 인체 — 장기들이 결합돼 살아 있는 유기체가 된다.
  • 우주 — 천체들은 중력으로 결합돼 있다(거리가 아무리 멀어도).

소프트웨어도 다르지 않다. 데이터베이스에 "강하게 결합된" 객체는 데이터베이스에 "강하게 연결된" 객체다. 두 마이크로서비스가 "느슨하게 결합됐다"는 말은 둘이 연결은 돼 있지만 의존이 얇다는 뜻일 뿐, 완전 독립이라는 뜻이 아니다.

핵심 통찰 — "결합 = 나쁘다"가 아니다. 결합 = 연결이고, 연결이 없으면 시스템은 존재할 수 없다. 진짜 질문은 "결합이 있냐 없냐"가 아니라 "어떤 종류의 결합이 얼마나 있냐"다.

비유로 쉽게 — 레고와 종이접기

레고 블록은 돌기(스터드)와 구멍(튜브)으로 결합된다. 결합 자체는 변하지 않지만, 어떻게 연결되는지(겹쳐 쌓는지·옆으로 붙이는지·플레이트로 잇는지)에 따라 만들 수 있는 모양이 달라진다.

반면 종이접기는 접힌 선으로 부분들이 결합된다. 한 접힌 선을 펴면 옆 선도 펴진다 — 강한 결합. 분리 가능한 레고와 달리 종이접기는 분리 자체가 형태 파괴다.

소프트웨어도 같은 기능을 레고처럼 만들 수도 있고 종이접기처럼 만들 수도 있다. 어떤 결합 방식을 고르느냐가 이 책의 주제다.

2. 결합의 규모 — 무엇이 결합을 강하게/약하게 만드는가

결합의 규모는 "두 구성요소가 얼마나 자주 함께 변경되어야 하느냐"로 측정된다. 두 가지 동인이 있다.

2.1 공유 수명주기 — 같이 빌드·테스트·배포되는가

같은 모놀리스 안의 모듈들은 수명주기가 같다. 한 곳을 고치면 전체를 다시 빌드·테스트·배포해야 한다. 별 서비스로 떼어 놓으면 각자 독립적으로 배포할 수 있다.

[ 강한 수명주기 결합 — 모놀리스 ]
┌─────────────────────────────────────────┐
│  하나의 배포 단위                       │
│  ┌──────────┐    ┌──────────────┐       │
│  │ Payments │    │ Authorization│       │
│  └──────────┘    └──────────────┘       │
└─────────────────────────────────────────┘
     같이 빌드·테스트·배포된다.

[ 약한 수명주기 결합 — 서비스 분리 ]
┌──────────────┐   ┌──────────────────────┐
│ Billing 서비스│   │ Identity & Access 서비스│
│  (Payments)  │   │   (Authorization)        │
└──────────────┘   └──────────────────────┘
   각자 독립 배포.

일상 비유 — 결혼한 부부의 일정과, 일주일에 한 번 만나는 친구 관계. 부부는 일정 변경 하나에도 양쪽이 다 영향받는다(강한 수명주기 결합). 친구는 약속 잡을 때만 동기화한다(약한 결합).

주의 — 캡슐화 경계 외에도 구조적·조직적 요소(팀 구조, 배포 파이프라인, 레포지토리 분리 여부 등)가 수명주기 결합에 영향을 준다. 8장 '거리'에서 더 자세히 다룬다.

2.2 공유 지식 — 서로에 대해 얼마나 알아야 하는가

같은 기능(고객 저장)을 세 가지 방식으로 설계해 보자. 결합의 형태가 어떻게 달라지는지 본다.

잘못된 예 / 올바른 예 — 저장소 인터페이스 3단계

A. 가장 많은 지식 공유 — 데이터베이스 종류까지 노출

# 잘못된 예: CustomersService 가 "MySQL"이라는 구체 제품을 안다
class CustomersService:
    def __init__(self, repo: MySQLRepository):  # ← 구체 클래스에 의존
        self.repo = repo

    def register(self, customer):
        self.repo.execute_sql(
            "INSERT INTO customers (name, email) VALUES (%s, %s)",
            (customer.name, customer.email)
        )

문제: MySQL을 PostgreSQL이나 DynamoDB로 바꾸려면 CustomersService 코드까지 손대야 한다. 데이터베이스 선택이라는 지식이 캡슐화되지 않고 밖으로 새어 나와 있다.

B. 중간 단계 — 추상 인터페이스, 하지만 여전히 "SQL"을 안다

class IRepository:  # 추상 인터페이스
    def begin_transaction(self): ...
    def execute_sql(self, query, params): ...  # ← SQL 가정이 남음
    def commit(self): ...
    def rollback(self): ...

class CustomersService:
    def __init__(self, repo: IRepository):  # 인터페이스에 의존
        self.repo = repo

    def register(self, customer):
        self.repo.execute_sql(
            "INSERT INTO customers (name, email) VALUES (%s, %s)",
            (customer.name, customer.email)
        )

진전: 구체 데이터베이스 제품은 모르지만, 여전히 "관계형 DB 집단"을 가정한다. MongoDB·Redis 같은 키-값/문서 저장소로 바꾸면 깨진다.

C. 가장 적은 지식 공유 — 의도만 노출

class IRepository:
    def save(self, entity): ...        # 무엇으로 저장하는지 모름
    def query(self, criteria): ...     # 어떻게 검색하는지 모름

class CustomersService:
    def __init__(self, repo: IRepository):
        self.repo = repo

    def register(self, customer):
        self.repo.save(customer)  # SQL 도, 명령 형태도 안다는 가정 없음

    def find_active(self):
        return self.repo.query({"status": "active"})

결과: 저장소가 MySQL이든 MongoDB든 메모리든 CustomersService 입장에선 동일. 공유되는 지식은 "고객 저장이 필요하다"는 의도뿐이다.

참고 — A→B→C로 갈수록 공유 지식이 줄어 변경 전파 빈도도 준다. 다만 무조건 C가 좋은 건 아니다. 너무 추상화하면 정작 필요한 정보까지 가려진다(예: 트랜잭션 경계를 통제해야 하는 금융 시스템). 적절한 수준을 고르는 게 설계다.

2.3 암묵적 지식 — 더 위험한 형태

명시적으로 공유 안 했는데도 암묵적으로 가정하는 지식이 있다.

  • "운영체제는 Linux일 거야" — Windows 배포에서 폭발
  • "이 API는 항상 5초 안에 응답해" — 네트워크 지연에서 폭발
  • "이 큐는 절대 비어 있지 않아" — 새벽 4시 트래픽에서 폭발

실무 체크 포인트 — 코드 리뷰에서 "이건 항상 X일 거야"가 들리면, 그 가정이 인터페이스에 명시돼 있는지 확인하라. 명시 안 됐다면 그게 암묵적 결합이다.

3. 지식의 흐름 — 상위와 하위

결합된 두 구성요소 사이에는 지식 흐름의 방향이 있다.

지식 흐름 ← ← ← ← ← ← ← ←
┌──────────────────┐    ┌──────────┐
│ Distribution     │ ──▶│   CRM    │
│  (하위/downstream)│    │(상위/upstream)│
└──────────────────┘    └──────────┘
종속성 화살표 → → → → → → → → →
  • 상위 (Upstream) — 기능을 제공. CRM은 고객 데이터를 제공한다.
  • 하위 (Downstream) — 기능을 소비. Distribution은 CRM을 호출해 고객 정보를 가져온다.

종속성과 지식은 방향이 반대다. 하위가 상위의 인터페이스를 알고 있어야 호출할 수 있기 때문이다.

비유 — 식당과 손님

식당(상위)은 메뉴라는 인터페이스를 제공한다. 손님(하위)이 식당의 주방 내부 구조를 알 필요는 없지만 메뉴는 알아야 주문할 수 있다. 메뉴가 바뀌면 손님은 그걸 알게 되어야 한다(지식 흐름: 식당 → 손님). 손님이 바뀌어도 식당은 모른다(역방향 흐름 없음).

소프트웨어에서 상위 인터페이스 변경이 위험한 이유가 여기다. 하위는 자기도 모르는 사이 깨질 수 있다.

4. 시스템이란 무엇인가

도넬라 메도즈의 정의: "무언가를 달성하는 방식으로 구성된 상호 연결된 요소들의 집합."

세 구성요소가 서로 의존한다.

        목적
       ↗   ↘
   필요로  허용
     ↗         ↘
구성요소 ─── 상호작용
  • 구성요소 — 부분(서비스·모듈·클래스·메서드·문장).
  • 상호작용 — 부분들이 어떻게 협력하는가(=결합).
  • 목적 협력하는가(비즈니스 가치).

세 중 하나를 바꾸면 나머지가 영향받는다. 기능 추가(목적 변경) → 새 모듈 필요(구성요소 변경) → 모듈 간 호출 변경(상호작용 변경).

루스 맬런(Ruth Malan)의 통찰 — "시스템 설계는 본질적으로 경계(내부에 있는 것, 외부에 있는 것, 걸쳐 있는 것, 그 사이를 이동하는 것)와 상충 관계에 관한 것이다." 상자(구성요소)만큼이나 상자 사이의 선(상호작용=결합)을 정교하게 설계해야 한다.

시스템의 프랙털 본질

소프트웨어 시스템은 시스템의 시스템이다.

[ 회사 전체 ]
    └─ [ Processor 서비스 ] ← 자체로도 시스템
            └─ [ DataAccess 클래스 ] ← 자체로도 시스템
                    └─ [ NextRequest() 메서드 ] ← 자체로도 시스템
                            └─ [ if/return 문장들 ]

같은 결합 원리가 모든 층위에 적용된다. 메서드 안의 if 분기끼리도 결합돼 있고, 마이크로서비스 사이의 메시지 큐도 결합돼 있다. 이 프랙털 관점은 12장에서 본격적으로 다룬다.

5. 결합은 도구다 — 필수 vs 우연

이 장의 최종 메시지를 다시 정리하면:

구분 의미 처방
필수 결합 시스템 목적상 반드시 필요한 연결 (예: 주문 → 결제) 신중하게 관리
우연 결합 설계 부주의·기술 선택 미스로 우연히 생긴 연결 (예: 화면 컴포넌트가 DB 스키마를 직접 안다) 제거

참고 — 흔한 오해: "모든 의존성을 0으로 줄이자." 불가능하고 무용하다. 협력 없는 부품은 시스템이 아니라 부품 더미다. 목표는 결합 제거가 아니라 결합 질의 제어다.

실무 예제 — 우연 결합을 제거하는 한 줄 리팩토링

# 잘못된 예: HTTP 핸들러가 DB 스키마를 직접 의존
@app.post("/orders")
def create_order(request):
    db.execute(
        "INSERT INTO orders (user_id, items_json) VALUES (?, ?)",
        (request.user_id, json.dumps(request.items))
    )
    return {"status": "ok"}

문제: 컬럼명을 items_jsonitems로만 바꿔도 핸들러가 깨진다. DB 스키마라는 지식이 핸들러 안으로 새 나왔다.

# 올바른 예: 도메인 레이어가 스키마를 캡슐화
@app.post("/orders")
def create_order(request):
    order = Order.create(user_id=request.user_id, items=request.items)
    order_repo.save(order)
    return {"status": "ok"}

# order_repo 안에서만 컬럼명을 안다
class OrderRepo:
    def save(self, order: Order):
        db.execute(
            "INSERT INTO orders (user_id, items_json) VALUES (?, ?)",
            (order.user_id, order.items_serialized())
        )

핸들러는 "주문을 저장한다"만 알고, 컬럼명은 OrderRepo만 안다. 스키마 변경이 더 이상 핸들러를 깨뜨리지 않는다.

6. (선택) 기계공학에서 배우는 비유 — 허용 오차

기계공학에서 두 부품을 결합할 때 절단면을 완벽하게 같게 만들기는 비싸고 종종 불가능하다. 그래서 의도적으로 허용 오차 (Tolerance)를 둔다.

  • 허용 오차가 너무 작으면 — 작은 결함에도 결합이 안 됨(빡빡함).
  • 허용 오차가 너무 크면 — 헐거워서 흔들림(느슨함).

소프트웨어 인터페이스도 같다. 입력 검증을 너무 빡빡하게(예: 정규식 한 글자도 안 봐줌) 하면 정상 케이스도 거절된다. 너무 헐겁게(예: 검증 없음) 하면 잘못된 데이터가 시스템 깊숙이 들어와 폭발한다. 적절한 허용 오차가 핵심이다.

# 잘못된 예 — 너무 빡빡
def parse_phone(s: str) -> str:
    if not re.fullmatch(r"010-\d{4}-\d{4}", s):  # 하이픈 없는 입력 거절
        raise ValueError("Invalid")
    return s

# 잘못된 예 — 너무 헐거움
def parse_phone(s: str) -> str:
    return s  # 검증 없이 그대로 받음 → "abc123" 같은 게 DB까지 도달

# 올바른 예 — 적절한 허용 오차
def parse_phone(s: str) -> str:
    digits = re.sub(r"\D", "", s)  # 숫자만 추출 (하이픈 공백 괄호 허용)
    if not re.fullmatch(r"010\d{8}", digits):
        raise ValueError("Korean mobile required")
    return f"{digits[:3]}-{digits[3:7]}-{digits[7:]}"  # 정규형으로

핵심 개념 정리

개념 한 줄 설명
결합(coupling) "함께 고정" = 두 구성요소의 연결 관계. 제거가 아니라 관리 대상
공유 수명주기 같이 빌드·테스트·배포되는 정도. 모놀리스↑ / 별 서비스↓
공유 지식 한쪽이 다른 쪽의 무엇을 알아야 하는가. 적을수록 변경 전파↓
암묵적 결합 인터페이스에 명시되지 않은 가정. 가장 위험
상위/하위 상위=기능 제공, 하위=소비. 지식 흐름은 상위→하위
시스템 3요소 구성요소 + 상호작용(=결합) + 목적. 셋이 서로 의존
필수/우연 결합 목적상 필요한가, 설계 부주의인가. 후자만 제거
허용 오차 인터페이스가 받아들이는 입력의 폭. 너무 빡빡/헐거우면 둘 다 문제
캡슐화 경계 구성요소가 외부에 드러내는 면과 숨기는 면의 구분선
프랙털 본질 같은 결합 원리가 시스템 모든 계층(회사→서비스→클래스→메서드)에 적용됨

실무 체크리스트

  • [ ] 새 의존성을 추가하기 전에 "이건 필수 결합인가, 우연 결합인가" 자문했나?
  • [ ] 두 모듈을 같은 배포 단위에 둘지 결정할 때 수명주기 결합의 비용을 따져 봤나?
  • [ ] 인터페이스가 구체 제품(MySQL, Kafka)을 노출하는가, 의도(save/query, publish/subscribe)를 노출하는가?
  • [ ] 코드에 "이건 항상 X일 거야" 같은 암묵 가정이 인터페이스로 끌어올려져 있나?
  • [ ] 상위 인터페이스를 바꿀 때, 깨질 모든 하위를 식별했나?
  • [ ] 입력 검증의 허용 오차가 정상 케이스를 막거나 잘못된 케이스를 통과시키지 않는지 한계 케이스를 적어 봤나?
  • [ ] 시스템 설계 시 "상자(구성요소)"만큼이나 "상자 사이의 선(상호작용=결합)"에도 충분히 주의를 기울였나?

연습문제 (문제만 — 정답은 부록 D)

  1. 개념. 본문에 따르면 결합이 0인 시스템이 불가능하고 무용한가? 두 이유를 각각 한 문장으로 답하라.
  2. 분석. 같은 회사가 사내 결제 모듈을 (a) 모놀리스에 내장, (b) 별 마이크로서비스로 분리하는 두 방안을 두고 고민 중이다. 수명주기 결합 관점에서 둘의 차이를 설명하고, 어느 쪽이 "결합 강도가 높다"고 말할 수 있는지 답하라.
  3. 재설계. 다음 코드는 OrderService가 PostgreSQL이라는 구체 제품을 안다. 공유 지식을 줄이는 두 단계 리팩토링(B 단계·C 단계)을 코드로 보여라. python class OrderService: def __init__(self, db: PostgreSQLConnection): self.db = db def place(self, order): self.db.execute("INSERT INTO orders ...")
  4. 판단. 한 팀이 "마이크로서비스 간 직접 HTTP 호출 대신 카프카 메시지 큐로 다 바꾸자"고 한다. 이게 항상 결합을 줄이는 행동인가? 카프카 도입이 새로 만드는 암묵적 결합의 예를 두 가지 들어라.
  5. 적용. 일상 시스템 하나(예: 학교의 식권 발급 절차)에서 상위/하위 관계와 지식 흐름을 식별하라. 누가 어떤 인터페이스를 통해 무엇을 알고 있어야 하는가?

최신 동향 (2026-05 기준)

최신 동향 (검증 2026-05-26) — 결합·시스템 설계의 원리는 이 책 내용이 그대로 유효하다. 다만 결합을 측정·시각화하는 도구가 빠르게 성숙 중이다.

  • 모듈 간 결합 자동 시각화 도구 보편화. 정적 분석으로 import/호출 그래프를 그려 우연 결합 핫스폿을 찾는다. Sourcegraph · Sourcetrail · CodeScene 같은 도구가 자주 쓰인다.
  • 모놀리스 → 마이크로서비스 분리 가이드는 더 신중해졌다. 분리 후 수명주기 결합은 줄지만 지식 결합(스키마·계약·관측)이 오히려 복잡해질 수 있다는 사례가 누적돼, Microservices.io 같은 카탈로그가 "Strangler Fig·Service Decomposition" 패턴을 기준으로 자리잡았다.

부록 A. 용어 사전

한글 용어 원문 영문명 의미
결합 Coupling 두 구성요소가 연결돼 서로 영향을 주는 관계
공유 수명주기 Shared Lifecycle 함께 빌드·테스트·배포되는 결합 형태
공유 지식 Shared Knowledge 한쪽이 다른 쪽 내부에 대해 알아야 하는 양
캡슐화 경계 Encapsulation Boundary 모듈이 외부에 드러내는 면과 숨기는 면의 구분선
상위 Upstream 기능을 제공하는 쪽
하위 Downstream 기능을 소비하는 쪽
지식 흐름 Knowledge Flow 인터페이스로 노출된 정보의 이동 방향(상위→하위)
시스템 System "무언가를 달성하는 방식으로 구성된 상호 연결된 요소들의 집합" (메도즈)
필수 결합 Essential Coupling 시스템 목적상 반드시 필요한 결합
우연 결합 Accidental Coupling 설계 부주의로 생긴 결합. 제거 대상
허용 오차 Tolerance 결합부가 받아들이는 변화의 폭
암묵적 지식 Implicit Knowledge 인터페이스에 명시되지 않은 채 공유되는 가정
프랙털 구조 Fractal Structure 같은 패턴이 다른 규모에서 반복되는 구조. 소프트웨어 계층 전체에 같은 결합 원리 적용

부록 B. 핵심 비교표

결합의 두 동인 비교

구분 공유 수명주기 공유 지식
측정 기준 같이 빌드·배포 빈도 인터페이스로 노출된 정보 양
결정 요소 배포 단위(모놀리스/서비스) 추상화 수준(구체 제품/추상 의도)
강함 사례 같은 모놀리스의 두 모듈 MySQLRepository 직접 의존
약함 사례 별 마이크로서비스로 분리 IRepository(save/query) 의존

같은 기능, 다른 공유 지식 — 3단계 비교

단계 인터페이스 CustomersService가 아는 것 변경 영향
A MySQLRepository.execute_sql(...) DB 제품(MySQL) + SQL 문법 DB 제품 바꾸면 깨짐
B IRepository.execute_sql(...) DB 집단(관계형) + SQL 문법 NoSQL로 바꾸면 깨짐
C IRepository.save(e) / query(c) "저장이 필요하다"는 의도뿐 거의 안 깨짐

필수 결합 vs 우연 결합 비교

구분 필수 결합 우연 결합
발생 원인 시스템 목적에서 비롯 설계 부주의·기술 선택 실수
예시 주문 서비스 → 결제 서비스 호출 HTTP 핸들러가 DB 컬럼명 직접 참조
처방 신중하게 관리 제거

부록 C. 추천 참고 자료 & 링크

Tier 1 공식·표준 (생존 확인 2026-05-26)

자료 링크
책 공식 (제이펍) jpub.kr
원서 출판사 — Manning Balancing Coupling in Software Design
마이크로서비스 패턴 카탈로그 (Chris Richardson) microservices.io/patterns
메도즈 — Thinking in Systems chelseagreen.com
사이드 자료 — Ruth Malan 시스템 설계 ruthmalan.com

책 다른 장 안내

자료 설명
2장 커네빈 프레임워크로 복잡성 정의
3장 자유도·제약으로 본 복잡성과 상호작용
4장 모듈성 — 결합의 반대편
8장 (별권) "거리" — 수명주기 결합의 구조적 요인
12장 (별권) 소프트웨어 설계의 프랙털 기하학

부록 D. 연습문제 풀이

  1. (불가능: 시스템 자체가 성립 안 됨 / 무용: 부품 더미일 뿐 목적 달성 불가) 두 구성요소가 협력하려면 최소한의 지식 공유가 필요하다. 결합이 0이면 호출도 메시지도 못 보내 시스템이 동작하지 않는다. 또한 메도즈 정의상 시스템은 상호 연결된 요소들의 집합이므로, 연결이 0이면 정의상 시스템이 아닌 부품 더미다 — 목적을 달성할 수 없다.

  2. (b 마이크로서비스 분리가 수명주기 결합이 약함, 즉 a 모놀리스가 결합 강도가 높다) 모놀리스 내장(a)은 결제 모듈을 전체 시스템과 함께 빌드·테스트·배포해야 하므로 수명주기 결합이 강하다. 마이크로서비스 분리(b)는 결제 서비스를 독립적으로 배포할 수 있어 수명주기 결합이 약하다. 따라서 "결합 강도가 높다"는 표현은 a에 해당한다. 단, 지식 결합은 별개 차원으로 같이 봐야 한다(분리해도 API 계약은 공유).

  3. (B 단계 = 관계형 가정 유지 추상화, C 단계 = 의도만 노출)

```python # B 단계: 관계형 DB 추상화 — SQL은 여전히 노출 class IOrderDb: def execute(self, sql, params): ...

class OrderService: def init(self, db: IOrderDb): self.db = db def place(self, order): self.db.execute("INSERT INTO orders ...", (order.id, ...))

# C 단계: 의도만 — 저장 메커니즘 완전 캡슐화 class IOrderRepo: def save(self, order: Order): ...

class OrderService: def init(self, repo: IOrderRepo): self.repo = repo def place(self, order): self.repo.save(order) ```

  1. (아니다 — 새로운 암묵적 결합이 생긴다) 카프카 도입은 동기 호출 결합은 줄이지만, ① 메시지 스키마 공유 — 발행자가 필드 이름이나 타입을 바꾸면 모든 구독자가 깨진다(인터페이스가 분산 시스템 전체에 퍼져 더 위험). ② 토픽 의미 가정 — 구독자는 "이 토픽은 항상 주문 생성 이벤트"라고 암묵 가정한다. 발행자가 의미를 슬쩍 바꾸면(예: 결제 완료도 같은 토픽으로) 조용히 잘못 동작한다. 즉 동기 결합비동기 + 스키마 + 의미 결합으로 분산됐을 뿐 양적 결합은 늘 수 있다.

  2. (예시 — 식권 발급) 학생(하위)은 식당(상위)이 정한 식권 발급 인터페이스(가격·결제 수단·발급 위치·운영 시간)를 알아야 식권을 받을 수 있다. 식당은 학생 개개인을 모르지만(역방향 흐름 없음), 가격·메뉴 같은 공개 정보는 학생에게 흘러간다(지식 흐름: 식당 → 학생). 학생이 모르는 내부 지식(원가 구조·재고 관리)은 캡슐화돼 있다. 식당이 가격을 바꾸면 학생은 영향받지만, 학생이 동아리에 가입해도 식당은 영향받지 않는다. 이게 상위·하위·지식 흐름의 비대칭이다.

난이도
에피소드
질문
카드를 로딩 중...
답변

클릭하거나 Space를 눌러 뒤집기

0 / 0
학습 진도 0%
이동   Space 뒤집기   R 셔플